package com.xinQing.blogme.conf.security;

import com.xinQing.blogme.util.Check;
import com.xinQing.blogme.util.Response;
import com.xinQing.blogme.util.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

/**
 * Security
 *
 * Created by null on 2017/2/22.
 *
 * 集成JWT
 *
 * Modify by null on 2017/8/3
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${http.permitMvcMatcherUrls}")
    private String[] permitMvcMatcherUrls;

    @Value("${http.login.loginPage}")
    private String loginPage;

    @Value("${http.logout.logoutUrl}")
    private String logoutUrl;

    @Value("${http.logout.clearAuthentication}")
    private boolean clearAuthentication;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests()
                    // 不过滤的请求
                    .mvcMatchers(permitMvcMatcherUrls).permitAll()
                    .anyRequest().authenticated()
                .and()
                .logout()
                    .logoutRequestMatcher(new AntPathRequestMatcher(logoutUrl, HttpMethod.POST.name()))
                    .clearAuthentication(clearAuthentication)
                    .logoutSuccessHandler((request, response, authentication) -> {
                        if (Check.isNull(jwtTokenManager().getAuthentication(request))) {
                            Response.writeJsonAndFlush(response, Result.fail(HttpStatus.FORBIDDEN.value(), "需要jwt验证信息！", null));
                            return;
                        }
                        Response.writeJsonAndFlush(response, Result.ok("注销成功！", null));
                    })
                .and()
                // 基于token，所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                // 添加一个过滤器 所有访问 /login 的请求交给 JWTLoginFilter 来处理 这个类处理所有的JWT相关内容
                .addFilterBefore(jwtLoginFilter(), UsernamePasswordAuthenticationFilter.class)
                // 添加一个过滤器验证其他请求的Token是否合法
                .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * 全局安全方法必须配置authenticationManagerBean
     *
     * @return AuthenticationManagerDelegator
     * @throws Exception 异常信息
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 自定义AuthenticationProvider
        auth
                .authenticationProvider(authenticationProvider());
    }

    /**
     * 自定义身份认证Provider
     * 默认是DaoAuthenticationProvider
     * 实现AuthenticationProvider，自定义自己的身份认证Provider
     *
     * @return LimitLoginAuthenticationProvider 对登录失败尝试限制
     */
    @Bean
    public AuthenticationProvider authenticationProvider() {
        JWTAuthenticationProvider authenticationProvider = new JWTAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService());
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    }

    @Bean
    public JWTLoginFilter jwtLoginFilter() throws Exception {
        JWTLoginFilter jwtLoginFilter = new JWTLoginFilter(loginPage, authenticationManager());
        jwtLoginFilter.setJwtTokenManager(jwtTokenManager());
        return jwtLoginFilter;
    }

    @Bean
    public JWTAuthenticationFilter jwtAuthenticationFilter() {
        JWTAuthenticationFilter jwtAuthenticationFilter = new JWTAuthenticationFilter();
        jwtAuthenticationFilter.setJwtTokenManager(jwtTokenManager());
        return jwtAuthenticationFilter;
    }

    @Bean
    public JWTTokenManager jwtTokenManager() {
        JWTTokenManager jwtTokenManager = new JWTTokenManager();
        jwtTokenManager.setUserDetailsService(userDetailsService());
        return jwtTokenManager;
    }

    /**
     * 自定义UserDetailsService
     * 主要是根据username获取UserDetails信息
     * 跟Shiro的Realm类似
     *
     * @return MyUserDetailsServiceImpl
     */
    @Bean
    public UserDetailsService userDetailsService() {
        return new MyUserDetailsServiceImpl();
    }


    /**
     * 加密
     *
     * @return BCryptPasswordEncoder
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder(9);
    }
}